//
// 家庭自动化部分
//
package home

import (
	"adai.design/homeserver/log"
	"time"
	"adai.design/homeserver/members"
	"adai.design/homeserver/home/data"
	"encoding/json"
	"fmt"
	"github.com/globalsign/mgo/bson"
	"adai.design/homeserver/db"
)

const (
	// 位置自动化,如 家庭成员达到，离开
	AutoLocation = "location"
	// 时间自动化,如 工作日早上8:00 周末早上8:30
	AutoTimer = "timer"
	// 属性自动化,如 大门打开，室温超过38摄氏度
	AutoCharacteristic = "characteristic"

	// 有效时间段
	ConditionTimePeriod = "time"
	ConditionCharacteristic = "characteristic"
	ConditionLocation = "location"
)

type Trigger struct {
	Id			string		`json:"id" bson:"id"`

	// 标题和副标题有手机创建自动化时生成
	Title 		string		`json:"title" bson:"title"`
	SubTitle 	string		`json:"subtitle" bson:"subtitle"`

	// 是否有效
	IsEnable 		bool		`json:"enable" bson:"enable"`

	Events 		[]*event		`json:"events" bson:"events"`
	Conditions 	[]*condition	`json:"conditions,omitempty" bson:"conditions,omitempty"`

	// 自动化触发之后需要执行的场景和动作
	Scenes 		[]string		`json:"scenes,omitempty" bson:"scenes,omitempty"`
	Actions 	[]*Action		`json:"actions,omitempty" bson:"actions,omitempty"`
}

func (t *Trigger) checkCondition(h *Home) bool {
	if t.Conditions == nil || len(t.Conditions) == 0 {
		return true
	}

	for _, c := range t.Conditions {
		switch c.Type {
		case ConditionTimePeriod:
			if period, ok := c.data.(*conditionPeriod); ok {
				if period.check(time.Now()) == false {
					return false
				}
			}

		case ConditionCharacteristic:
			if char, ok := c.data.(*conditionCharacteristic); ok {
				if char.check(h) == false {
					return false
				}
			}

		case ConditionLocation:
			if location, ok := c.data.(*conditionLocation); ok {
				if location.check(h) == false {
					return false
				}
			}
		}
	}
	return true
}

// 定时器触发自动化检查
func (t *Trigger) checkTimerTrigger(h *Home, now time.Time) bool {
	if t.IsEnable == false {
		return false
	}
	var timer *eventTimer
	for _, event := range t.Events {
		if event.Type == AutoTimer {
			if tm, ok := event.data.(*eventTimer); ok {
				if tm.check(now) == true {
					timer = tm
					break
				}
			}
		}
	}
	if timer == nil {
		return false
	}

	// 自动化执行条件检查
	if t.checkCondition(h) == false {
		return false
	}

	// 执行场景
	t.executeTrigger(h)
	// 推送给用户
	push := &members.APNSAlert{
		Title: timer.title(),
		Body: t.SubTitle,
	}
	h.pushNotifyToMember(push)
	return true
}

// 配件属性触发条件
func (t *Trigger) checkCharacteristicTrigger(h *Home, char *data.Characteristic) bool {
	var echar *eventCharacteristic
	for _, event := range t.Events {
		if event.Type == AutoCharacteristic {
			if c, ok := event.data.(*eventCharacteristic); ok {
				if c.check(h, char) == true {
					echar = c
					break
				}
			}
		}
	}
	if echar == nil {
		return false
	}

	if t.checkCondition(h) == false {
		return false
	}

	// 自动化执行条件检查
	if t.checkCondition(h) == false {
		return false
	}

	// 执行场景
	t.executeTrigger(h)
	// 推送给用户
	push := &members.APNSAlert{
		Title: echar.title(h),
		Body: t.SubTitle,
	}
	h.pushNotifyToMember(push)
	return true
}

func (t *Trigger) checkLocationTrigger(h *Home, member *Member) bool {
	var levent *eventLocation
	for _, event := range t.Events {
		if event.Type == AutoLocation {
			if c, ok := event.data.(*eventLocation); ok {
				if c.check(h, member) == true {
					levent = c
					break
				}
			}
		}
	}
	if levent == nil {
		return false
	}

	if t.checkCondition(h) == false {
		return false
	}

	// 自动化执行条件检查
	if t.checkCondition(h) == false {
		return false
	}

	// 执行场景
	t.executeTrigger(h)
	// 推送给用户
	push := &members.APNSAlert{
		Title: levent.title(h, member),
		Body: t.SubTitle,
	}
	h.pushNotifyToMember(push)
	return true
}


func (t *Trigger) executeTrigger(home *Home) error {
	actions := make([]*Action, 0)
	// 收集场景需要执行的所有动作
	for _, sid := range t.Scenes {
		scene := home.sceneManager.findSceneById(sid)
		if scene != nil {
			actions = append(actions, scene.Actions...)
		}
	}
	actions = append(actions, t.Actions...)
	return home.containerManager.executeActions(home, actions...)
}

// 自动化管理
type TriggerManager struct {
	triggers []*Trigger
	ticker 	chan time.Time
}

func (tm *TriggerManager) run() {
	triggers := make([]*Trigger, 0)
	for _, trigger := range tm.triggers {
		for _, event := range trigger.Events {
			switch event.Type {
			case AutoLocation:
				event.data = NewEventLocation(event.Desc)
			case AutoTimer:
				event.data = newEventTimer(event.Desc)
			case AutoCharacteristic:
				event.data = newEventCharacteristic(event.Desc)
			default:
				log.Info("unknown event type(%s)", event.Type)
			}
		}

		for _, condition := range trigger.Conditions {
			switch condition.Type {
			case ConditionTimePeriod:
				condition.data = newConditionPeriod(condition.Desc)
			case ConditionCharacteristic:
				condition.data = newConditionCharacteristic(condition.Desc)
			case ConditionLocation:
				condition.data = newConditionLocation(condition.Desc)
			default:
				log.Info("unknown condition type(%s)", condition.Type)
			}
		}
		triggers = append(triggers, trigger)
	}
	tm.triggers = triggers

	// 定时自动化临时解决方案
	// 每一分钟滴答检查一次定时自动化
	tm.ticker = make(chan time.Time, 1)
	go func(ticker chan time.Time) {
		for {
			now := time.Now()
			duration := int(time.Second) * (60-now.Second()) - now.Nanosecond()
			select {
			case <- time.After(time.Duration(duration)):
				now = time.Now()
				//log.Debug("time now(%v)", now.Format("2006/01/02 15:04:05.9999"))
				ticker <- now
			}
		}
	}(tm.ticker)
}

func (tm *TriggerManager) findTriggerById(id string) *Trigger {
	for _, trigger := range tm.triggers {
		if trigger.Id == id {
			return trigger
		}
	}
	return nil
}

func (tm *TriggerManager) checkTimerTrigger(h *Home, now time.Time) {
	for _, t := range tm.triggers {
		t.checkTimerTrigger(h, now)
	}
}

func (tm *TriggerManager) checkCharacteristicTrigger(h *Home, char *data.Characteristic) {
	for _, t := range tm.triggers {
		t.checkCharacteristicTrigger(h, char)
	}
}

func (tm *TriggerManager) checkLocationTrigger(h *Home, m *Member) {
	db.HomeMemberMark(&db.HomeMemberState{
		HomeId: h.Id,
		MemberId: m.Id,
		State: m.State,
		Time: time.Now(),
	})
	for _, t := range tm.triggers {
		t.checkLocationTrigger(h, m)
	}
}

type MembershipPost struct {
	Id	 	string				`json:"membership"`
	State 		string			`json:"state"`
}

func (tm *TriggerManager) HandleLocation(h *Home, pkg *members.MessagePkg) error  {
	log.Info("handle location: %s", pkg.String())
	msg := pkg.Msg
	if msg.Method == members.MsgMethodPost {
		var post MembershipPost
		if err := json.Unmarshal(msg.Data, &post); err != nil {
			log.Debug("%s", err)
			return err
		}

		state := MemberStateUnknown
		if post.State == "arrive" {
			state =  MemberStateArrive
		} else if post.State == "leave" {
			state =  MemberStateLeave
		} else {
			return fmt.Errorf("membership(%s) state(%s) unsupported", post.Id, post.State)
		}

		m, err := h.memberManager.setState(post.Id, state)
		if err != nil {
			return err
		}

		tm.checkLocationTrigger(h, m)

		ack := &members.MessagePkg{
			Type: pkg.Type,
			Ctx: pkg.Ctx,
			Msg: &members.Message{
				Path: msg.Path,
				Method: msg.Method,
				State: "ok",
			},
		}
		h.replyMemberResult(ack)
	}
	return  nil
}

type AutomationPost struct {
	Type 	string				`json:"type"`
	Id 		string				`json:"id"`
	Data 	json.RawMessage		`json:"data"`
	Desc    bson.Raw
}

const (
	autoPostExecute 	= 	"execute"
	autoPostUpsert 		= 	"upsert" 	// 更新插入
	autoPostDelete 		= 	"delete"
)

func (tm *TriggerManager) Handle(h *Home, pkg *members.MessagePkg) error  {
	msg := pkg.Msg
	if msg.Path == PathLocation {
		return tm.HandleLocation(h, pkg)
	}

	if msg.Method == members.MsgMethodGet {
		buf, _ := json.Marshal(tm.triggers)
		ack := &members.MessagePkg{
			Type: pkg.Type,
			Ctx: pkg.Ctx,
			Msg: &members.Message{
				Path: msg.Path,
				Method: msg.Method,
				State: "ok",
				Home: h.Id,
				Data: buf,
			},
		}
		h.replyMemberResult(ack)

	// 更新执行或者删除
	} else if msg.Method == members.MsgMethodPost {
		var post AutomationPost
		if err := json.Unmarshal(msg.Data, &post); err != nil {
			log.Debug("%s", err)
			return err
		}

		if post.Type == autoPostExecute {
			if auto := tm.findTriggerById(post.Id); auto != nil {
				auto.executeTrigger(h)
			}
		}
	}
	return nil
}











